查看原文
其他

innodb_ruby:窥探InnoDB奥秘的神器

阿飞的博客 阿飞的博客 2021-08-29

一个InnoDB文件的解析工具,通过解析InnoDB文件,可以窥探其中很多的奥秘,绝对是一款非常优秀,高逼格的研究InnoDB的工具。但是作者不建议在生产环境使用,因为肯定有BUG,O(∩_∩)O哈哈~,作者写这个工具的主要目的是作为一个学习研究InnoDB的工具。

github地址

Github地址:https://github.com/jeremycole/innodb_ruby

安装

安装比较简单,执行命令gem install innodb_ruby即可。安装完成后,执行如下命令验证innodb_ruby是否安装成功:

[afei@afei app]# innodb_space --help

Usage: innodb_space <options> <mode>

Invocation examples:

  innodb_space -s ibdata1 [-T tname [-I iname]] [options] <mode>
  ... ...

如果提示ruby版本过低,从淘宝ruby镜像中下载高版本编译安装即可:

镜像地址:https://ruby.taobao.org/mirrors/ruby/ruby-1.9.3-p551.tar.gz
./configure --prefix=/app/ruby-2.2.10
make && make install

建议安装1.9.3-p551版本,因为当我安装了2.2.10版本的ruby后,执行innodb_space还是得到这样的错误信息:/usr/lib/ruby/gems/1.8/gems/bindata-2.4.3/lib/bindata.rb:5: BinData requires ruby >= 1.9.3。相关issue地址:https://github.com/jeremycole/innodb_ruby/issues/40。另外,安装ruby时,一步一步来,确保make以及make install时没有任何error信息。

准备数据

创建表插入数据之前,检查MySQL环境,MySQL建议5.5以上的版本,并且属性值:innodb_file_per_table=ON,innodb_file_format=Barracuda。接下来创建一张表,并借助存储过程插入一些数据:

-- 创建表
DROP TABLE IF EXISTS t_afei;
CREATE TABLE t_afei (
  id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  num int not null
) ENGINE=InnoDB;

-- 创建存储过程
DROP PROCEDURE IF EXISTS insertbatch;
CREATE PROCEDURE insertbatch()
BEGIN
DECLARE i INT;

  SET i=1;
  WHILE(i<=1000000DO
    INSERT INTO yyfax_afei.t_afei(num)VALUES(i);
    SET i=i+1
  END WHILE;
END;

-- 调用存储过程
call insertbatch();


用法示例

查看索引信息--name为索引名称,fseg为leaf表示属于叶子页的segment:

[afei@afei mysql]# innodb_space -s ibdata1 -T /data/mysql/yyfax_afei/t_afei space-indexes
id      name       root    fseg        used    allocated   fill_factor 
2405    PRIMARY    3       internal    3       3           100.00%     
2405    PRIMARY    3       leaf        1743    1760        99.03

命令中的/data/mysql是mysql的datadir,yyfax_afei是数据库名称,t_afei是表名称。建议切换到${datadir}目录下,那么执行命令时-T的值为yyfax_afei/t_afei即可。

index-level-summary

得到指定level的所有page信息:

# level=0的page太多,所以只统计行数:
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 0  index-level-summary | wc -l
1744
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 1  index-level-summary
page    index   level   data    free    records min_key 
36      2405    1       7813    8139    601     id=1
37      2405    1       14846   838     1142    id=344688
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 2  index-level-summary
page    index   level   data    free    records min_key 
3       2405    2       26      16226   2       id=1 
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 3  index-level-summary
page    index   level   data    free    records min_key

我们知道level值和索引树的高度是强相关的(叶子节点的level都是0),所以通过这个命令也可以知道InnoDB索引树高度。由上面执行命令的结果可知,level=3时没有任何数据,而level等于1和2都有,所以示例100w数据的表的索引树高度是3。

space-page-type-regions

统计相同类型页的连续空间,如下所示,start/end表示起始页,count总计占的页数。

[afei@afei mysql]# innodb_space -f /data/mysql/yyfax_afei/t_afei.ibd space-page-type-regions
start       end         count       type                
0           0           1           FSP_HDR             
1           1           1           IBUF_BITMAP         
2           2           1           INODE               
3           37          35          INDEX               
38          63          26          FREE (ALLOCATED)    
64          1774        1711        INDEX               
1775        1919        145         FREE (ALLOCATED)

通过结果可知,page为0,1,2类型名称分别是:FSP_HDR, IBUF_BITMAP, INODE。从page=3开始才是存放行数据和指针的页。

index-recurse

递归一个索引需要依赖一个ruby脚本文件simple_t_describer.rb,脚本内容如下:

class SimpleTDescriber < Innodb::RecordDescriber
  type :clustered
  key "i":INT:UNSIGNED:NOT_NULL
end

执行如下命令:

innodb_space -f /data/mysql/yyfax_afei/t_afei.ibd -r ~/simple_t_describer.rb -d SimpleTDescriber -p 3 index-recurse > recurseindex.log

这条命令会从root开始,全表扫描,以升序的方式遍历整个B+Tree索引树,遍历过程中会输出每个page以及指针的信息,包括叶子页和非叶子页,由于输出结果行过大(稍微大于表的行数),所以将结果重定向到一个recurseindex.log文件中:

# 表有100w数据,recurse index结果有1003491行数据
[afei@afei mysql]# wc -l recurseindex.log 
1003491 recurseindex.log

# 部分内容如下:
[afei@afei mysql]# head -10 recurseindex.log 
ROOT NODE #32 records, 26 bytes
  NODE POINTER RECORD ≥ (i=1) → #36
  INTERNAL NODE #36601 records, 7813 bytes
    NODE POINTER RECORD ≥ (i=1) → #4
    LEAF NODE #4287 records, 7462 bytes
      RECORD: (i=1) → ()
      RECORD: (i=2) → ()
      RECORD: (i=3) → ()
      RECORD: (i=4) → ()
      RECORD: (i=5) → ()

从结果可知,ROOT NODE即根节点是page=3的页。通过space-page-type-regions的分析可知,0,1,2这三个page类型是FSP_HDR, IBUF_BITMAP, INODE。其他的就是INTERNAL节点和LEAF节点。

page-records

统计某一页中的数据。以刚才index-recurse的结果为例,page=3是root页,这个page的数据如下,由结果可知,page=3有两个record,与recurseindex.log的结果是吻合的(ROOT NODE #3: 2 records, 26 bytes):

[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -p 3 page-records > 3.log
[afei@afei mysql]# cat 3.log 
Record 125: (id=1) → #36
Record 138: (id=344688) → #37

从这个结果也能看出来,root页是不保存具体数据,只保存主键索引的值和指针。

我们再从recurseindex.log中找几个LEAF节点,如下所示,page=1770,1771这些都是LEAF节点:

[afei@afei mysql]# grep "LEAF" recurseindex.log | tail -5
    LEAF NODE #1770: 574 records, 14924 bytes
    LEAF NODE #1771: 574 records, 14924 bytes
    LEAF NODE #1772: 574 records, 14924 bytes
    LEAF NODE #1773: 574 records, 14924 bytes
    LEAF NODE #1774: 379 records, 9854 bytes

其中,page=1770的部分数据如下,一个页大概能574条记录,而page=1774还没有填满,只有379条数据:

[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -p 1770 page-records > 1770.log
[afei@afei mysql]# head 1770.log 
Record 125: (id=997326) → (num=997326)

Record 151: (id=997327) → (num=997327)

Record 177: (id=997328) → (num=997328)

Record 203: (id=997329) → (num=997329)

Record 229: (id=997330) → (num=997330)

... ...

从这个结果也能看出来,叶子页会保存具体数据,不只是主键,非主键其他列(num列)的数据也有保存。

tree height

根据通过innodb_space得到的结果,我们大概能计算出索引树的高度,假设树的高度是h:

  • 百万级数据量的表,574^h=1000000。即h=2.17,所以百万级数据量且主键是int类型的表的索引树高度是3。

  • 十万级数据量的表,574^h=100000。即h=1.81,所以十万级数据量且主键是int类型的表的索引树高度是2。

下面是笔者对一张十万数据量的表做得测试,通过结果可以看出level=0或者1都有结果,level=2没有结果,所以索引树高度是2:

[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/tsw_afei -I PRIMARY -l 0  index-level-summary | wc -l
176
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/tsw_afei -I PRIMARY -l 1  index-level-summary 
page    index   level   data    free    records min_key 
3       2416    1       2275    13893   175     id=1 
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/tsw_afei -I PRIMARY -l 2  index-level-summary 
page    index   level   data    free    records min_key 
[afei@afei mysql]



    : . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存